package app.grapheneos.camera.ui.activities

import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.MediaStore
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.widget.ImageButton
import android.widget.ImageView
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.core.content.ContextCompat
import app.grapheneos.camera.R
import app.grapheneos.camera.util.getParcelableExtra
import java.io.ByteArrayOutputStream
import java.lang.Exception
import java.nio.ByteBuffer

open class CaptureActivity : MainActivity() {

    companion object {
        private const val CAPTURE_BUTTON_APPEARANCE_DELAY = 1000L
    }

    lateinit var outputUri: Uri
    lateinit var bitmap: Bitmap

    private lateinit var retakeIcon: ImageView

    private lateinit var flipCameraContent: ImageView
    lateinit var confirmButton: ImageButton

    fun isOutputUriAvailable(): Boolean {
        return ::outputUri.isInitialized
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        retakeIcon = findViewById(R.id.retake_icon)
        flipCameraContent = findViewById(R.id.flip_camera_icon_content)

        confirmButton = findViewById(R.id.confirm_button)

        getParcelableExtra<Uri>(intent, MediaStore.EXTRA_OUTPUT)?.let {
            outputUri = it
        }

        // Disable capture button for a while (to avoid picture capture)
        captureButton.isEnabled = false
        captureButton.alpha = 0f

        // Enable the capture button after a while
        Handler(Looper.getMainLooper()).postDelayed({

            captureButton.animate()
                .alpha(1f)
                .setDuration(300)
                .withEndAction {
                    captureButton.isEnabled = true
                }

        }, CAPTURE_BUTTON_APPEARANCE_DELAY)

        // Remove the modes tab layout as we do not want the user to be able to switch to
        // another custom mode in this state
        tabLayout.visibility = View.INVISIBLE

        // Remove the margin so that that the previewView can take some more space
        (previewView.layoutParams as MarginLayoutParams).let {
            it.setMargins(it.leftMargin, it.topMargin, it.rightMargin, 0)
        }

        // Bring the three buttons a bit down in the UI
        (threeButtons.layoutParams as MarginLayoutParams).let {
            it.setMargins(it.leftMargin, it.topMargin, it.rightMargin, 0)
        }

        // Change the drawable to cancel mode
        cancelButtonView.setImageResource(R.drawable.cancel)

        // Overwrite the existing listener to just close the existing activity
        // (in this case)
        cancelButtonView.setOnClickListener {
            finish()
        }

        // Remove the third option/circle from the UI
        thirdOption.visibility = View.INVISIBLE

        captureButton.setOnClickListener {
            if (timerDuration == 0) {
                takePicture()
            } else {
                if (cdTimer.isRunning) {
                    cdTimer.cancelTimer()
                } else {
                    cdTimer.startTimer()
                }
            }
        }

        retakeIcon.setOnClickListener {
            hidePreview()
        }

        confirmButton.setOnClickListener {
            confirmImage()
        }

        // Display the activity
    }

    fun takePicture() {

        showMessage(
            getString(R.string.capturing_image)
        )

        previewLoader.visibility = View.VISIBLE
        camConfig.imageCapture?.takePicture(
            ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageCapturedCallback() {
                override fun onCaptureSuccess(image: ImageProxy) {
                    super.onCaptureSuccess(image)
                    bitmap = imageProxyToBitmap(image, image.imageInfo.rotationDegrees.toFloat())
                    showPreview()
                    previewLoader.visibility = View.GONE
                    showMessage(getString(R.string.image_captured_successfully))

                    image.close()
                }

                override fun onError(exception: ImageCaptureException) {
                    super.onError(exception)
                    exception.printStackTrace()
                    showMessage(
                        getString(R.string.unable_to_capture_image)
                    )

                    finishActivity(RESULT_CANCELED)
                }
            }

        )
    }

    open fun showPreview() {

        camConfig.cameraProvider?.unbindAll()

        mainOverlay.setImageBitmap(bitmap)
        mainOverlay.visibility = View.VISIBLE

        settingsIcon.visibility = View.INVISIBLE

        flipCameraContent.visibility = View.INVISIBLE
        retakeIcon.visibility = View.VISIBLE

        captureButton.visibility = View.INVISIBLE
        confirmButton.visibility = View.VISIBLE

        previewView.visibility = View.INVISIBLE
    }

    open fun hidePreview() {
        camConfig.startCamera(true)

        settingsIcon.visibility = View.VISIBLE

        flipCameraContent.visibility = View.VISIBLE
        retakeIcon.visibility = View.INVISIBLE

        captureButton.visibility = View.VISIBLE
        confirmButton.visibility = View.INVISIBLE

        previewView.visibility = View.VISIBLE
    }

    private fun confirmImage() {

        val resultIntent = Intent("inline-data")

        if (::outputUri.isInitialized) {

            val bos = ByteArrayOutputStream()

            val cf: CompressFormat =
                if (outputUri.path?.endsWith(".png") == true) {
                    CompressFormat.PNG
                } else {
                    CompressFormat.JPEG
                }

            bitmap.compress(cf, 100, bos)
            val bitmapData: ByteArray = bos.toByteArray()

            var result = RESULT_CANCELED

            try {
                contentResolver.openOutputStream(outputUri)?.use {
                    it.write(bitmapData)
                }
                result = RESULT_OK
            } catch (e: Exception) {
                showMessage(getString(R.string.unable_to_save_image))
            }

            setResult(result)
        } else {
            bitmap = resizeImage(bitmap)
            resultIntent.putExtra("data", bitmap)
            setResult(RESULT_OK, resultIntent)
        }

        finish()
    }

    private fun imageProxyToBitmap(image: ImageProxy, rotation: Float): Bitmap {
        val planeProxy = image.planes[0]
        val buffer: ByteBuffer = planeProxy.buffer
        val bytes = ByteArray(buffer.remaining())
        buffer.get(bytes)
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.size).rotate(rotation)
    }

    private fun resizeImage(image: Bitmap): Bitmap {

        val width = image.width
        val height = image.height

        val scaleWidth = width / 10
        val scaleHeight = height / 10

        if (image.byteCount <= 1000000)
            return image

        return Bitmap.createScaledBitmap(image, scaleWidth, scaleHeight, false)
    }

    private fun Bitmap.rotate(degrees: Float): Bitmap {
        val matrix = Matrix().apply { postRotate(degrees) }
        return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
    }
}
